#include <stdio.h>
#include <stdlib.h>
#include "SysControlNod.h"

int taskCounter = 0;
int retValue;

unsigned int taskIDCounter = 0;
TUserList *userList=NULL;
TUserList *userListTail=NULL;
FILE *logFile;

HANDLE ghSemaphore;
HANDLE ghMutex;

HANDLE getMutex(void) {return ghMutex;}

void printInfo() {
	int i;
	for(i = 0; i < MAXCONN; ++i) {
		printf("%d ", userNumber[i]);
	}
	printf("\n");
}


int findEmptyConnect() {
	int ret = -1;
	int i;
	for(i = 0; i < MAXCONN; ++i) {
		if(PINodeSocket[i] == INVALID_SOCKET) {
			ret = i;
			break;
		}
	}
	return ret;
}

int findSocket(SOCKET s) {
	int i;
	int ret = -1;
	for(i = 0; i < MAXCONN; ++i) {
		if(PINodeSocket[i] == s) {
			ret = i;
			break;
		}
	}
	return ret;
}

int findLeastuserNumber() {
	int i ;
	int minV = 1050000000;
	int ret = -1;
	for(i = 0; i < MAXCONN; ++i) {
		if(PINodeSocket[i] != INVALID_SOCKET && userNumber[i] < minV) {
			minV = userNumber[i];
			ret = i;
		}
	}
	return ret;
}

void initSysControlNode() 
{
	int i;
	
	PINodeNumber = 0;
	wasVice = 0;

	for(i = 0; i < MAXCONN; ++i) {
		PINodeSocket[i] = INVALID_SOCKET;
		userNumber[i] = 0;
	}

    ghSemaphore = CreateSemaphore( 
        NULL,           // default security attributes
        0,  // initial count
        MAX_SEM_COUNT,  // maximum count
        NULL);          // unnamed semaphore
    if (ghSemaphore == NULL) {
        printf("CreateSemaphore error: %d\n", GetLastError());
        return;
    }

	ghMutex = CreateMutex( 
        NULL,              // default security attributes
        FALSE,             // initially not owned
        NULL);             // unnamed mutex
    if (ghMutex == NULL) {
        printf("CreateMutex error: %d\n", GetLastError());
        return;
    }

	ImBossEvent = CreateEvent(NULL, TRUE, FALSE, NULL);

	getList();

// 	logFile = fopen("log.txt", "w");
// 	setbuf(logFile, NULL);

}

void saveTask(Task *task) {
// 	fprintf(logFile, "A %d %d %d\n", task->id, task->info.op, task->info.val);
	if (task->info.op < PINODE_JOIN) //don't send additional ctrl command!
		sendBackupData(task, 'A');
}

void finishTask(Task *task) {
// 	fprintf(logFile, "D %d\n", task->id);
	if (task->info.op < PINODE_JOIN)
		sendBackupData(task, 'D');
}

void addTask(TOperationInfo op, int isTodoTask) {
	Task* task = (Task*)malloc(sizeof(Task));
	DWORD waitRes;
// 	printf("in add task\n");

	if (wasVice && !isTodoTask) {
		WaitForSingleObject(ImBossEvent, INFINITE);
	}

	++taskCounter;

	task->info.op = op.op;
	task->info.val = op.val;
	task->next = NULL;
	task->id = taskIDCounter++;
	
	if(headTask == NULL) {
		headTask = task;
		tailTask = task;
	} else {
// 		if (op.op == PINODE_QUIT) {
// 			waitRes = WaitForSingleObject(ghMutex, INFINITE);
// 			if (waitRes != WAIT_OBJECT_0)
// 				fprintf(stderr, "the synchronize may wrong!!!!!!!!!!!!!!!!");
// 			task->next = headTask;
// 			headTask = task;
// 			ReleaseMutex(ghMutex);
// 		}
// 		else {
			tailTask->next = task;
			tailTask = task;
// 		}
	}
	ReleaseSemaphore(ghSemaphore, 1, NULL);
	saveTask(task);
	Sleep(10);
}

Task* getTask() {
	Task* ret = NULL;
	DWORD waitRes;

// 	printf("in get task\n");

	waitRes = WaitForSingleObject(ghSemaphore, INFINITE);
	if (headTask == NULL || waitRes != WAIT_OBJECT_0)
		fprintf(stderr, "the synchronize wrong!!!!!!!!!!!!!!!!!");

	waitRes = WaitForSingleObject(ghMutex, INFINITE);
	if (waitRes != WAIT_OBJECT_0)
		fprintf(stderr, "the synchronize may wrong!!!!!!!!!!!!!!!!");
	ret = headTask;
	headTask = headTask->next;
	ReleaseMutex(ghMutex);

	return ret;
}

void showPlnode(void) {
	int i;
	printf("now I have %d PINodes, their IDs are ", PINodeNumber);
	for (i=0; i<MAXCONN; ++i)
		if (PINodeSocket[i] != INVALID_SOCKET)
			printf("%d ", i);
		printf("\n");
}

void dealTasks(void* args) {
	Task* task;
	int index = 0, i;
	int t1, t2;
	TOperationInfo op;
	
	char sendBuffer[MAXBUFFSIZE];
	char recvBuffer[MAXBUFFSIZE];
	int recvBytes;
	int cnt;

	//printf("deal tasks\n");
	while(1)
	{
		task = getTask(); //may block
// 		printf("get task: %d %d %d\n", task->id, task->info.op, task->info.val);
		
// 		for (i=0; i<MAXCONN; ++i)
// 			if (PINodeSocket[i] != INVALID_SOCKET)
// 				printf("sock %d's user number: %d\n", i, userNumber[i]);

// 		system("pause");
		if(task != NULL) {
			//deal with this task
			sprintf(sendBuffer, "%d %d", task->info.op, task->info.val);
// 			printf("send: %s\n", sendBuffer);
			if (PINodeNumber == 0 && task->info.op != SYSCTRL_QUIT) {
				printf("no payload node!\n");
				if (task->info.op == GETPNODEBYUSER || task->info.op == GETUSERNUMBYPNODE) {
					sprintf(sendBuffer, "%d %d %d#", task->info.op, task->info.val, -1);
					send(testerConn, sendBuffer, strlen(sendBuffer), 0);
				}

				finishTask(task);
				--taskCounter;
				free(task);
				task = NULL;

				continue;
			}
			switch(task->info.op) {
			case CONNECT_REQUEST:
				retValue = -1;
				sprintf(sendBuffer, "%d %d", GETPNODEBYUSER, task->info.val);
				for(i = 0; i < MAXCONN; ++i) {
					if(PINodeSocket[i] != INVALID_SOCKET) {
						send(PINodeSocket[i], sendBuffer, strlen(sendBuffer) + 1, 0);
						recv(PINodeSocket[i], recvBuffer, MAXBUFFSIZE, 0);
						sscanf(recvBuffer, "%d %d", &op.op, &op.val);
						if(op.op == YES) {
							retValue = i;
							break;
						}
					}
				}
				if (retValue != -1) {
					printf("user already serve by %d\n", retValue);
					break;
				}
	
				sprintf(sendBuffer, "%d %d", task->info.op, task->info.val);
				index = findLeastuserNumber();
				send(PINodeSocket[index], sendBuffer, strlen(sendBuffer) + 1, 0);
				recv(PINodeSocket[index], recvBuffer, MAXBUFFSIZE, 0);
//				printf("%s\n", recvBuffer);
				sscanf(recvBuffer, "%d %d", &op.op, &op.val);
				if(op.op == YES) {
// 					printf("connect request success!\n");
					userNumber[index]++;
				} else {
					fprintf(stderr, "connect request error!!!!!!!!!!!!!!!!!!\n");
				}

				break;
			case DELETE_REQUEST:
				op.op = GETPNODEBYUSER;
				op.val = task->info.val;
				sprintf(sendBuffer, "%d %d", op.op, op.val);
				index = -1;
				for(i = 0; i < MAXCONN; ++i) {
					if(PINodeSocket[i] != INVALID_SOCKET) {
						send(PINodeSocket[i], sendBuffer, strlen(sendBuffer) + 1, 0);
						recv(PINodeSocket[i], recvBuffer, MAXBUFFSIZE, 0);
						sscanf(recvBuffer, "%d %d", &t1, &t2);
						if(t1 == YES) {
							printf("very great + %d %d!\n", t1, i);
							index = i;
							break;
						}
					}
				}
				if(index != -1) {
					sprintf(sendBuffer, "%d %d", task->info.op, task->info.val);
					send(PINodeSocket[index], sendBuffer, strlen(sendBuffer) + 1, 0);
					recv(PINodeSocket[index], recvBuffer, MAXBUFFSIZE, 0);
					--userNumber[index];
// 					printf("delete!!! %d\n", index);
				}
				break;
			case GETPNODEBYUSER:
				retValue = -1;
				for(i = 0; i < MAXCONN; ++i) {
					if(PINodeSocket[i] != INVALID_SOCKET) {
						send(PINodeSocket[i], sendBuffer, strlen(sendBuffer) + 1, 0);
						recv(PINodeSocket[i], recvBuffer, MAXBUFFSIZE, 0);
// 						printf("OP GetPnodeByUser, recv: %s\n", recvBuffer);
						sscanf(recvBuffer, "%d %d", &op.op, &op.val);
						if(op.op == YES) {
							retValue = i;
							break;
						}
					}
				}
				printf("get pnode by user ret value: %d\n", retValue);
				sprintf(sendBuffer, "%d %d %d#", task->info.op, task->info.val, retValue);
				send(testerConn, sendBuffer, strlen(sendBuffer), 0);
				break;
			case GETUSERNUMBYPNODE:
				retValue = -1;
				if (PINodeSocket[task->info.val] != INVALID_SOCKET) {
					send(PINodeSocket[task->info.val], sendBuffer, strlen(sendBuffer) + 1, 0);
					recv(PINodeSocket[task->info.val], recvBuffer, MAXBUFFSIZE, 0);
					printf("recv buffer: %s\n", recvBuffer);
					sscanf(recvBuffer, "%d %d", &op.op, &op.val);
					if(op.op == YES) {
						retValue = op.val;
					}
				}
				printf("get user num by pnode ret value: %d\n", retValue);
				sprintf(sendBuffer, "%d %d %d#", task->info.op, task->info.val, retValue);
				send(testerConn, sendBuffer, strlen(sendBuffer), 0);
				break;
// 			case PINODE_JOIN:
// 				system("start PINode");
// 				break;
			case PINODE_QUIT:

				if (PINodeSocket[task->info.val] == INVALID_SOCKET) break;
				--PINodeNumber;
				send(PINodeSocket[task->info.val], sendBuffer, strlen(sendBuffer)+1, 0);

				//recv all users
				t2 = 0;
				while (t2 == 0) {
					printf("waiting data...\n");
					recvBytes = recv(PINodeSocket[task->info.val], recvBuffer, MAXBUFFSIZE, 0);
					printf("recv %d\n", recvBytes);
					recvBuffer[recvBytes] = 0;
					printf("recv data: %s\n", recvBuffer);
					if ((recvBytes-1>=0 && recvBuffer[recvBytes-1] == '#') || (recvBytes<0) ) {
						t2 = 1;
					}
					recvBuffer[recvBytes] = 0;
					cnt = 0;
					for (i=0; i<recvBytes; ++i)
						if (recvBuffer[i] == ' ') {
							sscanf(&recvBuffer[cnt], " %d", &t1);
							if (userList == NULL) {
								userList = (TUserList *)malloc(sizeof(TUserList));
								userList->id = t1;
								userList->next = NULL;
								userListTail = userList;
							}
							else {
								userListTail->next = (TUserList*)malloc(sizeof(TUserList));
								userListTail = userListTail->next;
								userListTail->next = NULL;
								userListTail->id = t1;
							}
							cnt=i+1;
						}
				}

				userNumber[task->info.val] = 1050000000;// for finding pinode with least users

				if (PINodeNumber == 0) {
					printf("no PInode any more, discard users.\n");
				}else {
					while (userList != NULL) {
						index = findLeastuserNumber();
						sprintf(sendBuffer, "%d %d", CONNECT_REQUEST, userList->id);
						send(PINodeSocket[index], sendBuffer, strlen(sendBuffer)+1, 0);
						
						recv(PINodeSocket[index], sendBuffer, MAXBUFFSIZE, 0);
						sscanf(sendBuffer, "%d %d", &op.op, &op.val);
						if(op.op == YES) {
							printf("connect request success!\n");
							userNumber[index]++;
						} else {
							fprintf(stderr, "connect request error!!!!!!!!!!!!!!!!!!\n");
						}
						userListTail = userList;
						userList = userList->next;
						free(userListTail);
					}
				}
				send(PINodeSocket[task->info.val], "#", 1, 0);
				closesocket(PINodeSocket[task->info.val]);
				PINodeSocket[task->info.val] = INVALID_SOCKET;
				userNumber[task->info.val] = 0;
				showPlnode();
				break;
			case SYSCTRL_QUIT:
				exit(0);
				break;
			default:
				printf("No such operation!\n");
				break;
			}
			
			finishTask(task);
			--taskCounter;
			free(task);
			task = NULL;
		}
	}
}

bool isBoss(SOCKET sock) {
	int i;
	char sendBuffer[MAXBUFFSIZE];

	if (role != ROLE_BOSS) {
		i = findBoss();
		sprintf(sendBuffer, "%d %d#", NO, i);
		send(sock, sendBuffer, strlen(sendBuffer), 0);
		return false;
	}
	else {
		sprintf(sendBuffer, "%d#", YES);
		send(sock, sendBuffer, strlen(sendBuffer), 0);
		return true;
	}
}

void waitingTask(void) {
	TOperationInfo op;
	SOCKET tempSock;
	int i, cnt=0, status;

	char recvBuffer[MAXBUFFSIZE];
	char okBuffer[10];
	char notOkBuffer[10];
	int recvBytes = 0;

	SOCKADDR_IN addrTester;
	int len=sizeof(SOCKADDR);
	unsigned short port = me.testPort;

	memset(okBuffer, 0, 10);
	sprintf(okBuffer, "%d  #", me.id);
	memset(notOkBuffer, 0, 10);
	sprintf(notOkBuffer, "%d#", NO);

	taskSockServer = createListenSocket2(port, &status);
	if (status == 0) {
		printf("my TESTER PORT is %d\n", port);
	}
	else {
		printf("my test port is in use. EXIT...\n");
		Sleep(5000);
		exit(0);
	}

	while (1) {
		printf("waiting tester...\n");
		tempSock = accept(taskSockServer, (SOCKADDR*)&addrTester, &len);
		if (tempSock != INVALID_SOCKET) {
			testerConn = tempSock;
			//	printf("accept socket error: %d\n", WSAGetLastError());
			printf("tester connected\n");
			printf("%d\n", testerConn);

			if (!isBoss(testerConn)) {
				closesocket(testerConn);
				continue;
			}
		}
		else {
			taskSockServer = createListenSocket2(port, &status);
		}

		while (1) {
			recvBytes = recv(testerConn, recvBuffer, MAXBUFFSIZE, 0);
			if (recvBytes == -1) {
				printf("testerConn socket error: %d\n", GetLastError());
				closesocket(testerConn);
				break;
			}
			recvBuffer[recvBytes] = 0;
// 			printf("receive data[len=%d]: %s\n", recvBytes, recvBuffer);
			
			cnt = 0;
			for (i=0; i<recvBytes; ++i)
				if (recvBuffer[i] == '#') {
// 					if (PINodeNumber == 0) {
// 						send(testerConn, notOkBuffer, strlen(notOkBuffer), 0);
// 						continue;
// 					}
					send(testerConn, okBuffer, strlen(okBuffer), 0);
					//				printf("%d, scanf string %s\n", cnt, &recvBuffer[cnt]);
					sscanf(&recvBuffer[cnt], " %d %d", &op.op, &op.val);
					//				printf("add task: %d %d\n", op.op, op.val);
					addTask(op, 0);
					
//					send(testerConn, okBuffer, strlen(okBuffer), 0);

					cnt=i+1;
				}
		}
	}
}

void waitingPINode(void *args) {
	int sockAddrLen = sizeof(SOCKADDR);
	SOCKADDR_IN  addrClient;
	
	SOCKET socketConn;
	
	char buffer[MAXBUFFSIZE];
	int recvBytes=0;
	int status = 0;
	int i;
	int readInUserNum;
	TOperationInfo op;
	
	SOCKET sockServer;
	unsigned short port = me.serverPort;

	if (args != NULL) sscanf(args, "%d", &port);
	sockServer = createListenSocket2(port, &status);
	if (status == 0) {
		printf("my SERVER PORT is %d\n", port);
	}
	else {
		printf("my server port is in use! EXIT...\n");
		Sleep(5000);
		exit(0);
	}
	
	while(PINodeNumber < MAXCONN) {
		socketConn=accept(sockServer, (SOCKADDR*)&addrClient, &sockAddrLen);

		if (!isBoss(socketConn)) {
			closesocket(socketConn);
			continue;
		}

		//asking info
		recvBytes = recv(socketConn, buffer, MAXBUFFSIZE, 0);
		if (recvBytes <= 0) continue;
		buffer[recvBytes] = 0;
// 		printf("recv : %s\n", buffer);
		sscanf(buffer, "%d", &status);
		if (status == NO) {//no info, create one
			i = findEmptyConnect();
			if(i != -1) {
				PINodeSocket[i] = socketConn;
				userNumber[i] = 0;
				sprintf(buffer, "%d %d", ADD_PINODE_SUCCESS, i);
				send(socketConn, buffer, MAXBUFFSIZE, 0);
			} else {
				sprintf(buffer, "%d %d", ADD_PINODE_FAIL, 0);
				send(socketConn, buffer, MAXBUFFSIZE, 0);
			}
		}
		else if (status == YES) {
			sscanf(buffer, "%d %d %d", &status, &i, &readInUserNum);
			if (i<0 || i>MAXCONN) {
				printf("in waiting plnode, recv bad info: %s\n", buffer);
				continue;
			}
			if (PINodeSocket[i] != INVALID_SOCKET)
				fprintf(stderr, "conflict PINode id!!!!!!!!!!!!!!!!!!");
			
			PINodeSocket[i] = socketConn;
			userNumber[i] = readInUserNum;
			printf("recovery pinode %d: usernumber %d\n", i, readInUserNum);
		}
		else if (status == PINODE_QUIT) {
			closesocket(socketConn);
			sscanf(buffer, " %d %d", &op.op, &op.val);
			addTask(op, 0);
			continue;
		}

		++PINodeNumber;
		showPlnode();

	}
}

void registerMe(void *args) {
	SOCKET tmp;
	SOCKET sock = *((SOCKET*)args);
	int sockAddrLen = sizeof(SOCKADDR);
	SOCKADDR_IN  addrClient;

	while (1) {
		tmp = accept(sock, (SOCKADDR*)&addrClient, &sockAddrLen);
		closesocket(tmp);
	}
}

void findMe(void) {
	int i;
	SOCKET tempSocket;
	SOCKET *paramSock;

	while (1) {
		for(i = 0; i < SERVER_LIST_SIZE; ++i) {
			tempSocket = createConnectSocket(serverList[i].registerPort, serverList[i].addr);
			if(tempSocket != INVALID_SOCKET) {
				closesocket(tempSocket);
			} else {
				me = serverList[i];
				break;
			}
		}
		if (i>=SERVER_LIST_SIZE) {
			fprintf(stderr, "too many servers!!!!!!!!!!!!!!!!! EXIT...");
			Sleep(5000);
			exit(0);
		}
		tempSocket = createListenSocket(me.registerPort);
		if (tempSocket == INVALID_SOCKET) {
			printf("someone take off my port! find another.\n");
		}
		else {
			paramSock = (SOCKET *)malloc(sizeof(SOCKET));
			*paramSock = tempSocket;
			_beginthread(registerMe, 0, paramSock);
			break;
		}
	}
}

void printServerInfo2(Network_Node_Info node) {
	printf("server info:\n\ttest port: %d\n\tserver port: %d\n\tregister port: %d\n\tcomm port: %d\n\tbackup port: %d\n\tID: %d\n",
		node.testPort, node.serverPort, node.registerPort, node.commPort, node.bakcupPort, node.id);
}

int main(int argc, char *argv[]) {
	int t;
	char buf[MAXBUFFSIZE];

	startUpNetwork();

	initSysControlNode();

	if (argc >= 2) {
		sscanf(argv[1], " %d", &t);
		if (t>=0) me = serverList[t];
		else findMe();
	}
	else
		findMe();

	printf("%d %d\n", SERVER_LIST_SIZE, me.id);
	printServerInfo2(me);
	
	if (argc>=4)
		sprintf(buf, "%s %s", argv[2], argv[3]);
	else if (argc>=3)
		sprintf(buf, "%s -1", argv[2]);


	_beginthread(initBackupModule, 0, (argc>=3) ? buf : NULL);
	_beginthread(waitingPINode, 0, NULL);
	_beginthread(dealTasks, 0, NULL);
	
	waitingTask();

	return 0;
}
